/*
  Copyright 2007 Anders Båtstrand

      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.

 */

package org.dyndns.anderlin.jmplayer.client.ui;

import org.dyndns.anderlin.jmplayer.model.PieceCategory;
import org.dyndns.anderlin.jmplayer.service.PieceCategoryService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.binding.form.HierarchicalFormModel;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.richclient.application.PageComponentContext;
import org.springframework.richclient.application.event.LifecycleApplicationEvent;
import org.springframework.richclient.application.support.AbstractView;
import org.springframework.richclient.command.CommandGroup;
import org.springframework.richclient.command.support.AbstractActionCommandExecutor;
import org.springframework.richclient.command.support.GlobalCommandIds;
import org.springframework.richclient.dialog.CompositeDialogPage;
import org.springframework.richclient.dialog.ConfirmationDialog;
import org.springframework.richclient.dialog.TabbedDialogPage;
import org.springframework.richclient.dialog.TitledPageApplicationDialog;
import org.springframework.richclient.form.FormModelHelper;
import org.springframework.richclient.progress.TreeStatusBarUpdater;
import org.springframework.richclient.tree.FocusableTreeCellRenderer;
import org.springframework.richclient.util.PopupMenuMouseListener;
import org.springframework.util.Assert;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;

/**
 * Created by IntelliJ IDEA.
 * User: anderlin
 * Date: May 31, 2007
 * Time: 10:53:58 AM
 * To change this template use File | Settings | File Templates.
 */
public class PieceCategoryManagerView extends AbstractView implements InitializingBean, ApplicationListener {

    private JTree pieceCategoryTree;

    private DefaultTreeModel pieceCategoryTreeModel;

    private PieceCategoryService pieceCategoryService;

    private DeleteExecutor deleteExecutor = new DeleteExecutor();

    private PropertiesExecutor propertiesExecutor = new PropertiesExecutor();

    private PieceCategoryPropertiesExecutor pieceCategoryPropertiesExecutor = new PieceCategoryPropertiesExecutor();

    public void setPieceCategoryService(PieceCategoryService pieceCategoryService) {
        this.pieceCategoryService = pieceCategoryService;
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(pieceCategoryService, "A pieceCategoryService must be provided.");
    }

    protected JComponent createControl() {
        JPanel view = new JPanel(new BorderLayout());
        createPieceCategoryManagerTree();
        JScrollPane sp = new JScrollPane(pieceCategoryTree);
        view.add(sp, BorderLayout.CENTER);
        return view;
    }

    private void createPieceCategoryManagerTree() {
        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("PieceCategorys");
        Collection<PieceCategory> pieceCategorys = pieceCategoryService.getAllPieceCategories();
        for (PieceCategory pieceCategory : pieceCategorys) {
            DefaultMutableTreeNode pieceCategoryNode = new DefaultMutableTreeNode(pieceCategory);
            // This would have loaded ALL images and imageVersion. Instead loadon demand
            /* for (Image image : pieceCategory.getImages()) {
               DefaultMutableTreeNode imageNode = new DefaultMutableTreeNode(image);
               pieceCategoryNode.add(imageNode);
               for (ImageVersion imageVersion : image.getVersions()) {
                   imageNode.add(new DefaultMutableTreeNode(imageVersion));
               }
           } */
            rootNode.add(pieceCategoryNode);
        }
        this.pieceCategoryTreeModel = new DefaultTreeModel(rootNode);
        this.pieceCategoryTree = new JTree(pieceCategoryTreeModel);
        pieceCategoryTree.setShowsRootHandles(true);
        pieceCategoryTree.addTreeSelectionListener(new TreeStatusBarUpdater(getStatusBar()) {
            public String getSelectedObjectName() {
                PieceCategory selectedPieceCategory = getSelectedPieceCategory();
                if (selectedPieceCategory != null) {
                    return selectedPieceCategory.getName();
                }
                return "PieceCategorys";
            }
        });
        pieceCategoryTree.addTreeSelectionListener(new TreeSelectionListener() {
            public void valueChanged(TreeSelectionEvent e) {
                updateCommands();

                // hour-glass while fetching
                pieceCategoryTree.setCursor(new Cursor(Cursor.WAIT_CURSOR));
                /**
                 // fetch children of the selected object
                 if (getSelectedPieceCategory() != null) {
                 PieceCategory p = getSelectedPieceCategory();
                 DefaultMutableTreeNode n = getSelectedPieceCategoryNode();
                 for (Image i : p.getImages()) {
                 n.add(new DefaultMutableTreeNode(i));
                 }
                 } else if (getSelectedImage() != null) {
                 Image p = getSelectedImage();
                 DefaultMutableTreeNode n = getSelectedImageNode();
                 for (ImageVersion i : p.getVersions()) {
                 n.add(new DefaultMutableTreeNode(i));
                 }
                 }    **/
                // finnished
                pieceCategoryTree.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
        });
        pieceCategoryTree.addMouseListener(new PopupMenuMouseListener() {
            protected boolean onAboutToShow(MouseEvent e) {
                return !isRootOrNothingSelected();
            }

            protected JPopupMenu getPopupMenu() {
                return getSelectedPieceCategory() != null ? createPieceCategoryPopupContextMenu() : null;
            }
        });
        pieceCategoryTree.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1 && propertiesExecutor.isEnabled()) {
                    propertiesExecutor.execute();
                }
            }
        });
        pieceCategoryTree.setCellRenderer(getTreeCellRenderer());
        pieceCategoryTree.setRootVisible(true);
    }

    private void updateCommands() {
        int treeSelectionCount = pieceCategoryTree.getSelectionCount();
        if (isRootOrNothingSelected()) {
            deleteExecutor.setEnabled(false);
            propertiesExecutor.setEnabled(false);
        } else if (treeSelectionCount == 1) {
            deleteExecutor.setEnabled(true);
            propertiesExecutor.setEnabled(true);
        } else if (treeSelectionCount > 1) {
            deleteExecutor.setEnabled(true);
            propertiesExecutor.setEnabled(false);
        }
    }

    private boolean isRootOrNothingSelected() {
        return pieceCategoryTree.getSelectionCount() == 0
                || (pieceCategoryTree.getSelectionCount() == 1 && pieceCategoryTree.isRowSelected(0));
    }

    private PieceCategory getSelectedPieceCategory() {
        DefaultMutableTreeNode node = getSelectedPieceCategoryNode();
        if (node != null)
            return (PieceCategory) node.getUserObject();

        return null;
    }

    private DefaultMutableTreeNode getSelectedPieceCategoryNode() {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) pieceCategoryTree.getLastSelectedPathComponent();
        if (node == null || !(node.getUserObject() instanceof PieceCategory))
            return null;

        return node;
    }

    private DefaultTreeCellRenderer treeCellRenderer = new FocusableTreeCellRenderer() {
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
                                                      boolean leaf, int row, boolean hasFocus) {
            super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            if (node.isRoot()) {
                this.setIcon(getIconSource().getIcon("folder.icon"));
            } else if (node.getUserObject() instanceof PieceCategory) {
                PieceCategory pieceCategory = (PieceCategory) node.getUserObject();
                this.setText(pieceCategory.getName());
                this.setIcon(getIconSource().getIcon("pieceCategory.bullet"));
            }
            return this;
        }
    };

    public TreeCellRenderer getTreeCellRenderer() {
        return treeCellRenderer;
    }

    protected void registerLocalCommandExecutors(PageComponentContext context) {
        context.register(GlobalCommandIds.DELETE, deleteExecutor);
        context.register(GlobalCommandIds.PROPERTIES, propertiesExecutor);
    }

    /**
     * Handle an application event.
     *
     * @param e the event to respond to
     */

    public void onApplicationEvent(ApplicationEvent e) {
        if (e instanceof LifecycleApplicationEvent) {
            LifecycleApplicationEvent le = (LifecycleApplicationEvent) e;
            if (le.getEventType().equals(LifecycleApplicationEvent.CREATED) && le.objectIs(PieceCategory.class)) {
                if (pieceCategoryTree != null) {
                    DefaultMutableTreeNode root = (DefaultMutableTreeNode) pieceCategoryTreeModel.getRoot();
                    root.add(new DefaultMutableTreeNode(le.getObject()));
                    pieceCategoryTreeModel.nodeStructureChanged(root);
                }
            }
        }
    }

    private JPopupMenu createPieceCategoryPopupContextMenu() {
        // delete, addImage separator, properties
        CommandGroup group = getWindowCommandManager().createCommandGroup(
                "pieceCategoryViewTablePieceCategoryCommandGroup",
                new Object[]{"deleteCommand", "separator",
                        "propertiesCommand"});
        return group.createPopupMenu();
    }

    private class PropertiesExecutor extends AbstractActionCommandExecutor {
        public void execute() {
            if (getSelectedPieceCategory() != null) {
                pieceCategoryPropertiesExecutor.execute();
            }
        }
    }

    private class DeleteExecutor extends AbstractActionCommandExecutor {
        public void execute() {
            int pieceCategoryCount = 0;
            int imageCount = 0;
            int imageVersionCount = 0;
            final java.util.List<DefaultMutableTreeNode> nodesToDelete = new ArrayList<DefaultMutableTreeNode>();
            TreePath[] paths = pieceCategoryTree.getSelectionPaths();
            for (TreePath path : paths) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
                if (node.isRoot()) {
                    continue;
                }
                if (node.getUserObject() instanceof PieceCategory) {
                    pieceCategoryCount++;
                }
                nodesToDelete.add(node);
            }

            ConfirmationDialog dialog = new ConfirmationDialog() {
                protected void onConfirm() {
                    for (DefaultMutableTreeNode node : nodesToDelete) {
                        pieceCategoryTreeModel.removeNodeFromParent(node);
                        if (node.getUserObject() instanceof PieceCategory) {
                            pieceCategoryService.remove((PieceCategory) node.getUserObject());
                        }
                    }
                }
            };
            if (pieceCategoryCount > 0) {
                dialog.setTitle(getMessage("confirmDeletePieceCategoryDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeletePieceCategoryDialog.label"));
            } else if (imageCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteImageDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteImageDialog.label"));
            } else if (imageVersionCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteImageVersionDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteImageVersionDialog.label"));
            }

            dialog.showDialog();
        }
    }

    // TODO: Move all usefull classes out of this mess!
    private class PieceCategoryPropertiesExecutor extends AbstractActionCommandExecutor {

        private HierarchicalFormModel pieceCategoryFormModel;

        private PieceCategoryForm pieceCategoryForm;

        private CompositeDialogPage compositePage;

        public void execute() {
            final PieceCategory pieceCategory = getSelectedPieceCategory();
            pieceCategoryFormModel = FormModelHelper.createCompoundFormModel(pieceCategory);
            pieceCategoryForm = new PieceCategoryForm(FormModelHelper.createChildPageFormModel(pieceCategoryFormModel, null));

            compositePage = new TabbedDialogPage("pieceCategoryProperties");
            compositePage.addForm(pieceCategoryForm);

            TitledPageApplicationDialog dialog = new TitledPageApplicationDialog(compositePage, getWindowControl()) {
                protected void onAboutToShow() {
                    pieceCategoryForm.requestFocusInWindow();
                    setEnabled(compositePage.isPageComplete());
                }

                protected boolean onFinish() {
                    pieceCategoryFormModel.commit();
                    // also update the version
                    pieceCategoryService.saveOrUpdate(pieceCategory);
                    pieceCategoryTreeModel.nodeChanged(getSelectedPieceCategoryNode());
                    return true;
                }
            };
            dialog.showDialog();
        }
    }

}